package comm

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"log"

	"io"
	"math"
	"math/rand"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
	"unicode"

	geo "github.com/kellydunn/golang-geo"
	"go.mongodb.org/mongo-driver/bson"
)

var (
	milliSec int64 = getMilliSec() //毫秒数
)

func init() {
	go func() {
		for {
			time.Sleep(time.Millisecond)
			atomic.StoreInt64(&milliSec, getMilliSec())
		}
	}()
}

func FormatFloat(value float64) string {
	// 将浮点数转换为字符串
	str := strconv.FormatFloat(value, 'f', -1, 64)

	// 如果字符串包含小数点，且小数点后有非零数字，则保留两位小数
	if strings.Contains(str, ".") {
		parts := strings.Split(str, ".")
		if len(parts) == 2 && parts[1] != "00" {
			return fmt.Sprintf("%.2f", value)
		}
	}

	// 否则，只显示整数部分
	return fmt.Sprintf("%.0f", value)
}

// RandStringRunes 返回随机字符串
func RandStringRunes(n int) string {
	var letterRunes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

	rand.NewSource(time.Now().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func NumberToHex(n int64, l int) string {
	s := strconv.FormatInt(n, 16)
	for len(s) < l {
		s = "0" + s
	}
	return s
}

func QuickSortDes[T any](arr []T, f func(T) float64) []T {
	if len(arr) <= 1 {
		return arr
	}
	pivot := arr[0]
	var left, right []T
	for i := 1; i < len(arr); i++ {
		v := arr[i]
		if f(v) > f(pivot) {
			left = append(left, v)
		} else {
			right = append(right, v)
		}
	}
	left = QuickSortDes[T](left, f)
	right = QuickSortDes[T](right, f)
	return append(append(left, pivot), right...)
}

func QuickSortAsc[T any](arr []T, f func(T) float64) []T {
	if len(arr) <= 1 {
		return arr
	}
	pivot := arr[0]
	var left, right []T
	for i := 1; i < len(arr); i++ {
		v := arr[i]
		if f(v) < f(pivot) {
			left = append(left, v)
		} else {
			right = append(right, v)
		}
	}
	left = QuickSortAsc[T](left, f)
	right = QuickSortAsc[T](right, f)
	return append(append(left, pivot), right...)
}

// 需要传入有序数组
func SectionArray[T any](arr []T, f func(T) float64) (map[float64][2]int64, bool) {
	var sections = make(map[float64][2]int64)
	var same = false
	var start int64 = 0
	for i := 1; i < len(arr); i++ {
		if f(arr[i]) != f(arr[start]) {
			if int64(i)-start > 1 {
				same = true
			}
			score := f(arr[start])
			sections[score] = [2]int64{start, int64(i)}
			start = int64(i)
		}
	}

	sections[f(arr[start])] = [2]int64{start, int64(len(arr))}

	return sections, same
}

func ParseRankData(v bson.M) (int64, int64, string) {
	var score int64
	switch v["score"].(type) {
	case int64:
		score = v["score"].(int64)
	case int32:
		score = int64(v["score"].(int32))
	case float64:
		score = int64(v["score"].(float64))
	}

	uid := v["uid"].(int64)
	nickname := v["nickname"].(string)

	return score, uid, nickname
}

//func RandString(length int) string {
//	rand.NewSource(time.Now().UnixNano())
//	rs := make([]string, length)
//	for start := 0; start < length; start++ {
//		t := rand.Intn(3)
//		if t == 0 {
//			rs = append(rs, strconv.Itoa(rand.Intn(10)))
//		} else if t == 1 {
//			rs = append(rs, string(rand.Intn(26)+65))
//		} else {
//			rs = append(rs, string(rand.Intn(26)+97))
//		}
//	}
//	return strings.Join(rs, "")
//}

func RandomSmsCode(n int) string {
	var letterRunes = []rune("1234567890")

	rand.NewSource(time.Now().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func RandomInt(min, max int) int {
	if min == max {
		return min
	}
	rand.NewSource(time.Now().UnixNano())
	return rand.Intn(max-min+1) + min
}

func RandomArr[T any](arr []T) []T {
	rand.Seed(time.Now().UnixNano()) // 使用当前时间作为随机种子

	n := len(arr)
	for i := n - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		arr[i], arr[j] = arr[j], arr[i]
	}
	return arr
}

func RandFloats(min, max float32, n int) []float32 {
	res := make([]float32, n)
	for i := range res {
		res[i] = min + rand.Float32()*(max-min)
	}
	return res
}

func getMilliSec() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}

func GetNowSecond() int64 {
	return atomic.LoadInt64(&milliSec)
}

func GetMonthWeek(date time.Time) (int32, int32) {
	// 获取该日期是当月的第几天
	dayOfMonth := date.Day()
	month := date.Month()

	// 计算该日期是当月的第几周
	week := (dayOfMonth-1)/7 + 1

	return int32(month), int32(week)
}
func GetNowWeek() int32 {
	_, week := time.Now().ISOWeek()
	return int32(week)
}

func GetNowWeekDay() int32 {
	day := time.Now().Weekday()
	if day == time.Sunday {
		day = 7
	}
	return int32(day)
}

func GetThisMonthStart() time.Time {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
}

func GetLastMonthStart() time.Time {
	now := time.Now()

	// 计算上个月的开始时间
	lastMonthStart := now.AddDate(0, -1, 0)
	lastMonthStart = time.Date(lastMonthStart.Year(), lastMonthStart.Month(), 1, 0, 0, 0, 0, now.Location())

	return lastMonthStart
}

func GetLastMonthEnd() time.Time {
	now := time.Now()

	// 计算上个月的结束时间
	lastMonthEnd := now.AddDate(0, 0, 0)
	lastMonthEnd = time.Date(lastMonthEnd.Year(), lastMonthEnd.Month(), 1, 0, 0, 0, 0, now.Location())
	lastMonthEnd = lastMonthEnd.Add(-time.Second)
	return lastMonthEnd
}

func GetTimeZero(t time.Time) time.Time {
	return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}

func NextOpenTime(duration int64, offset int, hour int) int64 {
	now := time.Now()

	baseHour := now.Hour()
	if hour != 0 {
		baseHour = hour
	}

	current := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), 0, 0, time.Local).UnixNano() / 1e6
	start := time.Date(now.Year(), now.Month(), now.Day(), baseHour, offset, 0, 0, time.Local).UnixNano() / 1e6

	for {
		if start >= current && start-current >= 3*60*1000 {
			break
		}
		start += duration
	}

	return start
}

func SetDayTime(timeStr string, duration int64) (int64, error) {
	stArr := strings.Split(timeStr, ":")
	if len(stArr) != 3 {
		return 0, errors.New("invalid time string")
	}
	itArr := []int{}
	for _, v := range stArr {
		val, err := strconv.Atoi(v)
		if err != nil {
			return 0, err
		}
		itArr = append(itArr, val)
	}
	return time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), itArr[0], itArr[1], itArr[2], 0, time.Local).UnixNano()/1e6 + duration, nil
}

func SetNextHour() int64 {
	return time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour()+1, 0, 0, 0, time.Local).UnixNano() / 1e6
}

func IsSameDay(t1, t2 time.Time) bool {
	y1, m1, d1 := t1.Date()
	y2, m2, d2 := t2.Date()

	return y1 == y2 && m1 == m2 && d1 == d2
}

func DiffDays(t1, t2 time.Time) int {

	// 获取每个时间的当天0点时间
	startOfDay1 := time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
	startOfDay2 := time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)

	// 计算相差的天数
	daysDifference := int(startOfDay2.Sub(startOfDay1).Hours() / 24)
	return daysDifference
}

func Round(val float64, precision int) float64 {
	if precision == 0 {
		return math.Round(val)
	}
	p := math.Pow10(precision)
	return math.Round(val*10*p/10) / p
}

func Floor(val float64, precision int) float64 {
	if precision == 0 {
		return math.Floor(val)
	}
	p := math.Pow10(precision)
	return math.Floor(val*10*p/10) / p
}

func TimeStrToTick(t string) int64 {
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		fmt.Printf("load loc failed, err: %s/n", err.Error())
		return 0
	}

	tobj, err := time.ParseInLocation("2006-01-02 15:04:05", t, loc)
	if err != nil {
		fmt.Printf("parse time failed, err: %s/n", err.Error())
		return 0
	}

	return tobj.UnixNano() / 1e6
}

func subString(str string, start, end int) string {
	rs := []rune(str)
	length := len(rs)

	if start < 0 || start > length {
		panic("start is wrong")
	}

	if end < start || end > length {
		panic("end is wrong")
	}

	return string(rs[start:end])
}

func GetFileDir(path string) string {
	return subString(path, 0, strings.LastIndex(path, "/"))
}

type AarryT interface {
	uint64 | int | uint | string | int64 | int32 | Day
}

func ExistInArray[T AarryT](arr []T, val T) bool {
	for _, v := range arr {
		if v == val {
			return true
		}
	}
	return false
}

func RemoveDuplicatesInArray[T AarryT](arr []T) []T {
	// 创建一个 map 来记录数组中的元素
	elements := make(map[T]bool)
	// 创建一个切片来存储去重后的元素
	result := []T{}

	for _, v := range arr {
		// 如果元素没有在 map 中出现过，添加到结果中
		if !elements[v] {
			elements[v] = true
			result = append(result, v)
		}
	}
	return result
}

func RemoveInArray[T AarryT](arr []T, f func(T) bool) []T {
	for i := 0; i < len(arr); i++ {
		if f(arr[i]) {
			arr = append(arr[:i], arr[i+1:]...)
			i--
		}
	}
	return arr
}

func IndexInArray[T AarryT](arr []T, val T) int {
	for k, v := range arr {
		if v == val {
			return k
		}
	}
	return -1
}

type MapT interface {
	uint64 | int | uint | string
}

func GetMapKeys[T MapT, S MapT](m map[T]S) []T {
	keys := make([]T, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

type Number interface {
	uint64 | int64 | uint32 | int32 | uint | int | uint8 | int8
}

func StringToNumber[T Number](s string) T {
	num, _ := strconv.Atoi(s)
	return T(num)
}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// CreatNestedFile 给定path创建文件，如果目录不存在就递归创建
func CreatNestedFile(path string) (*os.File, error) {
	basePath := filepath.Dir(path)
	if !Exists(basePath) {
		err := os.MkdirAll(basePath, 0700)
		if err != nil {
			log.Println("无法创建目录，%s", err)
			return nil, err
		}
	}

	return os.Create(path)
}

func DiffIntArr[T AarryT](arr1 []T, arr2 []T) []T {
	res := make([]T, 0)
	tempMap1 := make(map[T]int)
	tempMap2 := make(map[T]int)

	//已数组最长的赋值tempMap1
	if len(arr1) > len(arr2) {
		for _, v := range arr1 { //标记数组内重复数据
			tempMap1[v]++
		}

		for _, v := range arr2 { //标记数组内重复数据
			tempMap2[v]++
		}
	} else {
		for _, v := range arr1 { //标记数组内重复数据
			tempMap2[v]++
		}

		for _, v := range arr2 { //标记数组内重复数据
			tempMap1[v]++
		}
	}

	for k, v := range tempMap1 {
		if tempMap2[k] != v {
			res = append(res, k)
		}
	}

	return res
}

func GetInternalIP() string {
	// 思路来自于Python版本的内网IP获取，其他版本不准确
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return ""
	}
	defer conn.Close()

	// udp 面向无连接，所以这些东西只在你本地捣鼓
	res := conn.LocalAddr().String()
	res = strings.Split(res, ":")[0]
	return res
}

func GetExternalIP() (string, error) {
	// 有很多类似网站提供这种服务，这是我知道且正在用的
	// 备用：https://myexternalip.com/raw （cip.cc 应该是够快了，我连手机热点的时候不太稳，其他自己查）
	response, err := http.Get("http://ip.cip.cc")
	if err != nil {
		return "", errors.New("external IP fetch failed, detail:" + err.Error())
	}

	defer response.Body.Close()
	res := ""

	// 类似的API应当返回一个纯净的IP地址
	for {
		tmp := make([]byte, 32)
		n, err := response.Body.Read(tmp)
		if err != nil {
			if err != io.EOF {
				return "", errors.New("external IP fetch failed, detail:" + err.Error())
			}
			res += string(tmp[:n])
			break
		}
		res += string(tmp[:n])
	}

	return strings.TrimSpace(res), nil
}

func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {

		return false
	}
	return s.IsDir()

}

//func Construct2DArray[T any](original []T, n int) ([][]T, []T) {
//	l := math.Floor(float64(len(original)) / float64(n))
//
//	res := make([][]T, int(l))
//	left := make([]T, 0)
//	var index int
//	for i := range res {
//		start := n * i
//		end := n*i + n
//		res[i] = original[start:end:end]
//		index = i
//	}
//
//	if (index+1)*n < len(original) {
//		left = original[(index+1)*n:]
//	}
//
//	return res, left
//}

func Construct2DArray[T any](original []T, n int) [][]T {
	l := math.Ceil(float64(len(original)) / float64(n))

	res := make([][]T, int(l))
	for i := range res {
		start := n * i
		end := n*i + n
		if end > len(original) {
			end = len(original)
		}
		res[i] = original[start:end:end]
	}

	return res
}

func DateTimeToStringID(baseTime time.Time) string { // time second
	Month := baseTime.Month()
	Dayy := baseTime.Day()
	hour := baseTime.Hour()
	minu := baseTime.Minute()
	secn := baseTime.Second()
	return fmt.Sprintf("%02d%02d%02d%02d%02d", Month, Dayy, hour, minu, secn)
}

func GetMatchIdByInsId(insid string) int {
	temp := strings.Split(insid, "_")
	if len(temp) == 0 {
		return 0
	}
	matchId, _ := strconv.Atoi(temp[0])
	return matchId
}

func CheckTodayTimeCycle(start string, end string) (bool, *time.Time) {
	now := time.Now()

	inputStart, err := time.Parse("15:04:05", start)
	if err != nil {
		return false, nil
	}

	inputEnd, err := time.Parse("15:04:05", end)
	if err != nil {
		return false, nil
	}

	timeStart := time.Date(now.Year(), now.Month(), now.Day(), inputStart.Hour(), inputStart.Minute(), inputStart.Second(), 0, time.Local)
	timeEnd := time.Date(now.Year(), now.Month(), now.Day(), inputEnd.Hour(), inputEnd.Minute(), inputEnd.Second(), 0, time.Local)

	if timeStart.UnixMilli() >= timeEnd.UnixMilli() {
		return false, nil
	}

	if timeStart.UnixMilli() <= now.UnixMilli() && now.UnixMilli() < timeEnd.UnixMilli() {
		return true, nil
	}

	if now.UnixMilli() < timeStart.UnixMilli() {
		return false, &timeStart
	}

	return false, nil

}

func SHA1(s string) string {
	o := sha1.New()
	o.Write([]byte(s))
	return hex.EncodeToString(o.Sum(nil))
}

// func CountCustomOpenTime(conf *match.Match) int64 {
// 	// 按开赛类型 计算比赛开始时间
// 	var openTime int64
// 	switch conf.CustomSubType {
// 	case CustomSubType_Once:
// 		// 计算开赛时间
// 		openTime = TimeStr2DateTime(fmt.Sprintf("%s %s", conf.MatchStartDay, conf.MatchStartTime))
// 	case CustomSubType_Custom:
// 		// 检查是否时今天
// 		day := time.Now().Weekday()
// 		if ExistInArray[Day](conf.TimeDays, Day(day)) {
// 			openTime, _ = SetDayTime(conf.MatchStartTime, 0)
// 		}
// 	}

// 	if openTime == 0 || openTime < GetNowSecond() {
// 		return 0
// 	}
// 	return openTime
// }

var timeLayout = "2006-01-02 15:04:05" //转化所需模板
var TimeLayout = "2006-01-02 15:04:05" //转化所需模板

func TimeStr2DateTime(datatime string) int64 {
	loc, _ := time.LoadLocation("Local")                          //重要：获取时区
	theTime, _ := time.ParseInLocation(timeLayout, datatime, loc) //使用模板在对应时区转化为time.time类型
	return theTime.UnixMilli()
}

func IsStringHasSpecialCode(str string) bool {
	for _, r := range str {
		if unicode.IsGraphic(r) && !unicode.IsLetter(r) && !unicode.IsDigit(r) {
			return true
		}
	}
	return false
}

func IsValidPhone(phoneStr string) bool {
	// 匹配规则
	reg := regexp.MustCompile("^1[3-9]\\d{9}$")
	// 返回 MatchString 是否匹配
	return reg.MatchString(phoneStr)
}

// 根据经纬度计算 p1 到 p2 的距离
func GetTowPointDistance(p1Lat, p1Lng, p2Lat, p2Lng float64) float64 {
	// Make a few points
	p := geo.NewPoint(p1Lat, p1Lng)
	p2 := geo.NewPoint(p2Lat, p2Lng)

	// Find the great circle distance between them
	return p.GreatCircleDistance(p2)
}

// T is any type
func SliceOfPtrToSliceOfVal[T any](s []*T) []T {
	var res []T           // create a new slice of type []T
	for _, p := range s { // iterate over the slice of type []*T
		res = append(res, *p) // dereference each pointer and append to the new slice
	}
	return res // return the new slice
}

// ExistIndex 判断切片中是否存在指定下标的数据
func ExistIndex(s interface{}, index int) bool {
	// 将切片转换为 reflect.Value 类型
	v := reflect.ValueOf(s)
	// 获取切片的长度
	length := v.Len()
	// 如果下标小于长度，返回 true
	if index < length {
		return true
	}
	// 否则返回 false
	return false
}

func NumberAdvance(n int64) int64 {
	count := 1
	for {
		if n/10 == 0 {
			break
		}
		n = n / 10
		count++
	}

	return int64(math.Pow10(count))
}

func ParseSliceIntToInt32(arr []int) []int32 {
	var int32Slice []int32

	for _, value := range arr {
		int32Value := int32(value)
		int32Slice = append(int32Slice, int32Value)
	}

	return int32Slice
}

func ParseSliceInt64ToInt32(arr []int64) []int32 {
	var int32Slice []int32

	for _, value := range arr {
		int32Value := int32(value)
		int32Slice = append(int32Slice, int32Value)
	}

	return int32Slice
}

func GenerateRandomString(length int) string {
	// 可以包含的字符集合
	// 可以包含的字符集合
	charSet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

	// 设置随机种子
	rand.Seed(time.Now().UnixNano())

	// 生成随机字符串
	var result strings.Builder
	for i := 0; i < length; i++ {
		randomIndex := rand.Intn(len(charSet))
		result.WriteByte(charSet[randomIndex])
	}

	return result.String()
}
